package logging

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"log/slog"
	"os"
	"path/filepath"
	"time"

	"github.com/gin-gonic/gin"
	"saltadmin/internal/config"
)

// Logger 封装 slog.Logger
type Logger struct {
	logger *slog.Logger
}

// NewLogger 创建新的日志记录器
func NewLogger(cfg config.LogConfig) *Logger {
	var logWriter io.Writer = os.Stdout

	// 如果配置为输出到文件
	if cfg.Output == "file" && cfg.OutputFile != "" {
		// 确保目录存在
		dir := filepath.Dir(cfg.OutputFile)
		if err := os.MkdirAll(dir, 0755); err != nil {
			fmt.Printf("创建日志目录失败: %v\n", err)
		}

		// 打开日志文件
		file, err := os.OpenFile(cfg.OutputFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			fmt.Printf("打开日志文件失败: %v\n", err)
		} else {
			logWriter = file
		}
	}

	// 创建日志处理器
	var handler slog.Handler
	if cfg.Format == "json" {
		handler = slog.NewJSONHandler(logWriter, &slog.HandlerOptions{
			Level:     getLogLevel(cfg.Level),
			AddSource: true,
		})
	} else {
		handler = slog.NewTextHandler(logWriter, &slog.HandlerOptions{
			Level:     getLogLevel(cfg.Level),
			AddSource: true,
		})
	}

	// 创建 logger 实例
	logger := slog.New(handler)

	return &Logger{
		logger: logger,
	}
}

// 获取日志级别
func getLogLevel(level string) slog.Level {
	switch level {
	case "debug":
		return slog.LevelDebug
	case "info":
		return slog.LevelInfo
	case "warn":
		return slog.LevelWarn
	case "error":
		return slog.LevelError
	default:
		return slog.LevelInfo
	}
}

// Debug 记录调试日志
func (l *Logger) Debug(msg string, args ...any) {
	l.logger.Debug(msg, args...)
}

// Info 记录信息日志
func (l *Logger) Info(msg string, args ...any) {
	l.logger.Info(msg, args...)
}

// Warn 记录警告日志
func (l *Logger) Warn(msg string, args ...any) {
	l.logger.Warn(msg, args...)
}

// Error 记录错误日志
func (l *Logger) Error(msg string, args ...any) {
	l.logger.Error(msg, args...)
}

// With 返回带有附加字段的日志记录器
func (l *Logger) With(args ...any) *Logger {
	return &Logger{
		logger: l.logger.With(args...),
	}
}

// TraceIDKey 是上下文中 traceID 的键
type TraceIDKey struct{}

// GenerateTraceID 生成一个随机的 traceID
func GenerateTraceID() string {
	b := make([]byte, 8)
	_, err := rand.Read(b)
	if err != nil {
		return time.Now().Format("20060102150405.000")
	}
	return hex.EncodeToString(b)
}

// TraceMiddleware 为每个请求生成 traceID 并添加到上下文中
func TraceMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 从请求头获取 traceID，如果没有则生成一个新的
		traceID := c.GetHeader("X-Trace-ID")
		if traceID == "" {
			traceID = GenerateTraceID()
			// 将 traceID 添加到响应头
			c.Header("X-Trace-ID", traceID)
		}

		// 将 traceID 添加到上下文中
		ctx := context.WithValue(c.Request.Context(), TraceIDKey{}, traceID)
		c.Request = c.Request.WithContext(ctx)

		c.Next()
	}
}

// GetTraceID 从上下文中获取 traceID
func GetTraceID(ctx context.Context) string {
	if ctx == nil {
		return ""
	}
	if traceID, ok := ctx.Value(TraceIDKey{}).(string); ok {
		return traceID
	}
	return ""
}

// GinLogger 创建 Gin 框架的日志中间件
func GinLogger(logger *Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 开始时间
		startTime := time.Now()
		path := c.Request.URL.Path
		query := c.Request.URL.RawQuery
		method := c.Request.Method

		// 获取 traceID
		traceID := GetTraceID(c.Request.Context())

		// 处理请求
		c.Next()

		// 结束时间
		endTime := time.Now()
		// 执行时间
		latency := endTime.Sub(startTime)
		// 状态码
		statusCode := c.Writer.Status()
		clientIP := c.ClientIP()

		// 构建日志字段
		logAttrs := []any{
			"trace_id", traceID,
			"status", statusCode,
			"method", method,
			"path", path,
			"query", query,
			"ip", clientIP,
			"latency", latency.String(),
			"user-agent", c.Request.UserAgent(),
		}

		// 根据状态码选择日志级别
		if len(c.Errors) > 0 {
			// 有错误，添加错误信息
			logAttrs = append(logAttrs, "errors", c.Errors.String())
			logger.Error("HTTP 请求失败", logAttrs...)
		} else if statusCode >= 500 {
			logger.Error("HTTP 请求", logAttrs...)
		} else if statusCode >= 400 {
			logger.Warn("HTTP 请求", logAttrs...)
		} else {
			logger.Info("HTTP 请求", logAttrs...)
		}
	}
}
